using System;
using System.Collections.Generic;
using Lucene.Net.Index;
using Lucene.Net.Documents;
using log4net;

namespace ODSearchEngine.Lucene
{
	internal class LuceneIndexEngine : IndexEngineBase, IDisposable
	{
		private static readonly ILog logger = LogManager.GetLogger(typeof(LuceneIndexEngine));
		private IndexWriter writer = null;
		private Dictionary<string, Field> fieldPool = null;
		
		public LuceneIndexEngine(ISearchEngine searchEngine) : base(searchEngine)
		{
			this.writer = new IndexWriter(Constants.indexDirectory, 
			                              Constants.defaultAnalyzer,
			                              true, 
			                              IndexWriter.MaxFieldLength.UNLIMITED);
			this.fieldPool = new Dictionary<string, Field>();
		}
		
		#region implemented abstract members of ODSearchEngine.IndexEngineBase
		
		public override void Init()
		{
			this.writer.Optimize();
		}
		
		public override void CreateIndex (bool recreate)
		{
			if(recreate)
			{
				writer.DeleteAll();
				writer.Commit();
			}
		}
		
		
		public override void IndexData (IDictionary<string, string> data)
		{	
			string idFieldValue = data[Constants.fieldID];
			foreach(var key in data.Keys)
			{
				if(key.Equals(Constants.fieldID))
					continue;
				Document document = new Document();
				Field idField = new Field(Constants.fieldID,
				                          idFieldValue,
				                          Field.Store.YES,
				                          Field.Index.NOT_ANALYZED);
				Field pNameField = new Field(Constants.fieldParameterName, 
				                             key.ToLower(), 
				                             Field.Store.YES, 
				                             Field.Index.NOT_ANALYZED);
				Field pValueField = new Field(Constants.fieldParameterValue,
				                              data[key].ToLower(),
				                              Field.Store.YES,
				                              Field.Index.ANALYZED);
				document.Add(idField);
				document.Add(pNameField);
				document.Add(pValueField);
				logger.DebugFormat("Indexing data. {0}", document);
				this.writer.AddDocument(document);
				this.writer.Commit();
			}			
		}
		
		
		public override int RemoveData (IDictionary<string, string> data)
		{
			throw new System.NotImplementedException();
		}
		
		
		public override void UpdateData (IDictionary<string, string> oldData, IDictionary<string, string> valuesToSet)
		{
			throw new System.NotImplementedException();
		}
		
		#endregion
		
		#region IDisposable implementation
		public void Dispose ()
		{
			this.writer.Commit();
			this.writer.Close();			
		}
		#endregion
		
		/// <summary>
		/// Gets a field from field pool. If field is not present in the pool, then it is created.
		/// Be sure to put received field back into pool, after document is written.
		/// TODO: Add check for special fields.
		/// </summary>
		/// <param name="fieldName">
		/// A <see cref="System.String"/> that specifies field name.
		/// </param>
		/// <returns>
		/// A <see cref="Lucene.Net.Documents.Field"/> from pool.
		/// </returns>
		private Field GetFieldFromPool(string fieldName)
		{
			Field result = null;
			if(!this.fieldPool.ContainsKey(fieldName))
			{
				result = new Field(fieldName, "", Field.Store.YES, Field.Index.ANALYZED);
			}
			else
			{
				lock(this.fieldPool)
				{
					result = this.fieldPool[fieldName];
					this.fieldPool.Remove(fieldName);
				}
			}
			return result;
		}
		
		private void PutFieldIntoPool(Field field)
		{
			lock(this.fieldPool)
			{
				if(!this.fieldPool.ContainsKey(field.Name()))
				{
					this.fieldPool.Add(field.Name(), field);
				}
			}
		}		
	}
}

